home *** CD-ROM | disk | FTP | other *** search
- /*
- ** AIFFGoodies
- **
- ** This module contains the routines that pertian to opening, reading and otherwise dealing
- ** with AIFF Files
- **
- ** (NMD) 9/16/91 created
- ** (NMD) 9/16/91 OpenAIFFFile
- ** (NMD) 9/16/91 GetAIFFHeaderInfo
- ** • Changed to take the paramBlock as an argument and return a long
- ** indicating the total length of the AIFF data.
- ** (NMD) 9/20/91 Fixed GetAIFFHeaderInfo to (properly) deal with only 8 bit monophonic samples.
- ** (NMD) 10/3/91 Changed GetAIFFHeaderInfo so it doesn't stomp on the samplePtr & lenght...
- ** (NMD) 11/29/91 RecordAIFFFile allows you to record an aiff file to disk...
- */
-
-
- #pragma load "MacHeaders"
-
- #include <FixMath.h>
-
- #ifndef __MAININCLUDES__
- #include "MainApp.h"
- #endif
-
-
- #define kChunkBufferSize 128
-
- #define kChunkHeaderSize 8
-
- enum { // Sample sizes
- k8bits = 8,
- k16bits = 16,
- k24bits = 24,
- k32bits = 32
- };
-
- // these are bit positions for a long flag
-
- #define kFORM (1<<0)
- #define kFormatVersion (1<<1)
- #define kCommon (1<<2)
- #define kSoundData (1<<3)
- #define kMarker (1<<4)
- #define kInstrument (1<<5)
- #define kMIDIData (1<<6)
- #define kAudioRecording (1<<7)
- #define kApplicationSpecific (1<<8)
- #define kComment (1<<9)
- #define kName (1<<10)
- #define kAuthor (1<<12)
- #define kCopyright (1<<13)
- #define kAnnotation (1<<14)
-
-
- typedef union {
- ChunkHeader generic;
- ContainerChunk container;
- FormatVersionChunk formatVersion;
- CommonChunk common;
- ExtCommonChunk extCommon;
- SoundDataChunk soundData;
- MarkerChunk marker;
- InstrumentChunk instrument;
- MIDIDataChunk midiData;
- AudioRecordingChunk audioRecording;
- CommentsChunk comments;
- TextChunk text;
- } ChunkTemplate, *ChunkTemplatePtr;
-
-
- //
- // Given an AIFF File gets the sample rate and other relavent pieces of information.
- // Leaves the file mark at the beginning of the AIFF Sound Data Chunk. Please note
- // that I'm placing some pretty severe restrictions on the types of samples that this
- // will actually deal with - 8 bit monophonic, to be more specific...
- //
- long GetAIFFHeaderInfo (short frefNum, SoundHeaderPtr theHeader)
- {
- OSErr err; // error container
- long filePosition; // current position in file
- long dataStart = 0; // where snd data starts
- long dataSize = 0; // how big snd data is
-
- long byteCount; // # if bytes to get from file
- char chunkBuffer[kChunkBufferSize]; // buffer for chunk data
-
- unsigned short chunkFlags = 0; // remember chunks we've seen
- ChunkTemplatePtr template; // ...for ease of access
-
- // Keep track of the beginning of the file
- err = GetFPos (frefNum, &filePosition);
- Assert ((err != noErr), "\pCouldn't get file position");
-
- // Now for the fun part ! We're going to assume that these are generic, non-compressed
- // AIFF files and parse accordingly...
-
- do {
-
- // Position ourselves at the beginning of the next chunk and read in
- // a hunk-o-data...
- err = SetFPos (frefNum, fsFromStart, filePosition);
- Assert ((err != noErr), "\pCouldn't set file position");
- if (err != noErr)
- break;
-
- byteCount = kChunkBufferSize;
- err = FSRead (frefNum, &byteCount, chunkBuffer); // read a chunk
- Assert ((err != noErr && err != eofErr), "\pBombed out reading file");
- if ( (err != noErr) && (err != eofErr) )
- break;
-
- // Now, position the template over the data...
- template = (ChunkTemplatePtr) chunkBuffer;
-
- // assume a failure and break out of the do {} while loop if the next case isn't found.
- // if the case is found and no other error is detected, then each case needs to set noErr
- err = badFileFormat;
- switch (template->generic.ckID) {
-
- case FORMID : // Format Version Chunk?
- DebugMessage ("\pfound the form chunk");
- Assert ((chunkFlags & kFORM), "\pOH NO!! Found a duplicate FORM Chunk!!");
- Assert ((template->container.formType != AIFFID), "\pthis isn't a non-compressed AIFF file");
-
- // make sure that this is a standard, noncompressed AIFF file.
- if ( !(chunkFlags & kFORM) && (template->container.formType == AIFFID) ) { // see if this chunk already exists
- chunkFlags |= kFORM; // otherwise mark it found
- // filePosition += template->container.ckSize + kChunkHeaderSize; // calculate next chunk's position
- filePosition += 12;
- err = noErr;
- }
- break;
-
-
- case FormatVersionID : // Format Version Chunk?
- DebugMessage ("\pfound the container chunk");
- Assert ((chunkFlags & kFormatVersion), "\pOH NO!! Found a duplicate format version Chunk!!");
-
- if ( !(chunkFlags & kFormatVersion) ) { // see if this chunk already exists
- chunkFlags |= kFormatVersion; // otherwise mark it found
- filePosition += template->formatVersion.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case CommonID :
- DebugMessage ("\pfound the common chunk");
- Assert ((chunkFlags & kCommon), "\pOH NO!! Found a duplicate common Chunk!!");
- Assert ((template->common.numChannels != 1), "\pToo many channels");
- Assert ((template->common.sampleSize != k8bits), "\pWrong sample size");
-
- if ( !(chunkFlags & kCommon)
- && (template->common.numChannels == 1)
- && (template->common.sampleSize == k8bits)) { // see if this chunk already exists
-
- chunkFlags |= kCommon; // otherwise mark it found
- filePosition += template->common.ckSize + kChunkHeaderSize; // calculate next chunk's position
-
- // Now that we theoretically have all the relavant information, put it into a header the sound manager
- // can deal with. Note that we're not returning the length of the file in theHeader->length, because it is
- // EXTREMELY unlikely that the AIFF file is small enough to feed the SM without resorting to multiple buffers.
- // • Note that we're not modifying the header's samplePtr or lenght, cause those may contain valid entries!!
- theHeader->sampleRate = X2Fix (template->common.sampleRate);
- theHeader->loopStart = 0;
- theHeader->loopEnd = 0;
- theHeader->encode = 0;
- theHeader->baseFrequency = 60;
-
- // Perhaps some explanation is in order here: we're interested in how many bytes of data we're going to
- // be dealing with later on. Since AIFF is interleaved, multiply the number of channels present by
- // the number of frames in the sample, by the number of bits per sample. Divide by 8 bits/byte and
- // you done got the number of bytes. So a stereo 8 bit sample actually looks like this:
- //
- // |------------- frame 1 -----------| |------------- frame 2 -----------| ...
- //
- // [{ch1 sample pt 1} {ch2 sample pt 1}] [{ch1 sample pt 2} {ch2 sample pt 2}] ...
-
- dataSize = (template->common.numChannels * template->common.numSampleFrames * template->common.sampleSize) / 8;
- err = noErr;
- }
- break;
-
-
- case SoundDataID:
- DebugMessage ("\pfound the sound chunk");
- Assert ((chunkFlags & kSoundData), "\pOH NO!! Found a duplicate sound data Chunk!!");
-
- if ( !(chunkFlags & kSoundData) ) { // see if this chunk already exists
-
- // Let's remember where the Sound Data starts, so we can find the position in the file later.
- // The mark will be positioned at the beginning of the chunk, so move 16 bytes past to get past
- // the header information to the data.
- dataStart = filePosition + 16;
-
- chunkFlags |= kSoundData; // otherwise mark it found
- filePosition += template->soundData.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
-
-
- //
- // The following list of chunks we don't care about, so we'll just skip over them.
- //
- case MarkerID:
- DebugMessage ("\pfound a marker chunk");
- Assert ((chunkFlags & kMarker), "\pOH NO!! Found a duplicate marker Chunk!!");
-
- if ( !(chunkFlags & kMarker) ) { // see if this chunk already exists
- chunkFlags |= kMarker; // otherwise mark it found
- filePosition += template->marker.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case CommentID:
- DebugMessage ("\pfound a comment chunk");
- Assert ((chunkFlags & kComment), "\pOH NO!! Found a duplicate comment Chunk!!");
-
- if ( !(chunkFlags & kComment) ) { // see if this chunk already exists
- chunkFlags |= kComment; // otherwise mark it found
- filePosition += template->marker.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case InstrumentID:
- DebugMessage ("\pfound an instrument chunk");
- Assert ((chunkFlags & kInstrument), "\pOH NO!! Found a duplicate instrument Chunk!!");
-
- if ( !(chunkFlags & kInstrument) ) { // see if this chunk already exists
- chunkFlags |= kInstrument; // otherwise mark it found
- filePosition += template->instrument.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case MIDIDataID:
- DebugMessage ("\pfound a MIDI Data chunk");
- Assert ((chunkFlags & kMIDIData), "\pOH NO!! Found a duplicate MIDI Chunk!!");
-
- if ( !(chunkFlags & kMIDIData) ) { // see if this chunk already exists
- chunkFlags |= kMIDIData; // otherwise mark it found
- filePosition += template->midiData.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case AudioRecordingID:
- DebugMessage ("\pfound an audio recording chunk");
- Assert ((chunkFlags & kAudioRecording), "\pOH NO!! Found a duplicate Audio Recording Chunk!!");
-
- if ( !(chunkFlags & kAudioRecording) ) { // see if this chunk already exists
- chunkFlags |= kAudioRecording; // otherwise mark it found
- filePosition += template->audioRecording.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case ApplicationSpecificID:
- DebugMessage ("\pfound an application specifi chunk");
- Assert ((chunkFlags & kApplicationSpecific), "\pOH NO!! Found a duplicate application specific Chunk!!");
-
- if ( !(chunkFlags & kApplicationSpecific) ) { // see if this chunk already exists
- chunkFlags |= kApplicationSpecific; // otherwise mark it found
- filePosition += template->generic.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case NameID:
- DebugMessage ("\pfound a name chunk");
- Assert ((chunkFlags & kName), "\pOH NO!! Found a duplicate name Chunk!!");
-
- if ( !(chunkFlags & kName) ) { // see if this chunk already exists
- chunkFlags |= kName; // otherwise mark it found
- filePosition += template->generic.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case AuthorID:
- DebugMessage ("\pfound an author chunk");
- Assert ((chunkFlags & kAuthor), "\pOH NO!! Found a duplicate author Chunk!!");
-
- if ( !(chunkFlags & kAuthor) ) { // see if this chunk already exists
- chunkFlags |= kAuthor; // otherwise mark it found
- filePosition += template->generic.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case CopyrightID:
- DebugMessage ("\pfound a copyright chunk");
- Assert ((chunkFlags & kCopyright), "\pOH NO!! Found a duplicate copyright Chunk!!");
-
- if ( !(chunkFlags & kCopyright) ) { // see if this chunk already exists
- chunkFlags |= kCopyright; // otherwise mark it found
- filePosition += template->generic.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- case AnnotationID:
- DebugMessage ("\pfound an uninteresting chunk");
- Assert ((chunkFlags & kAnnotation), "\pOH NO!! Found a duplicate annotation Chunk!!");
-
- if ( !(chunkFlags & kAnnotation) ) { // see if this chunk already exists
- chunkFlags |= kAnnotation; // otherwise mark it found
- filePosition += template->generic.ckSize + kChunkHeaderSize; // calculate next chunk's position
- err = noErr;
- }
- break;
-
-
- default : // Unrecognized?? croak.
- Assert (true, "\pran into an undefined chunk!!");
-
- // If we hit an unrecognized chunk, clear chunkFlags to drop out of the loop
- // with the assumed error code.
- chunkFlags = 0;
- break;
- }
-
- // We're only done when the the FORM, FormatVersion, Common and Sound Data chunks. This is only
- // true of this incarnation - your needs may vary, so you'll have to modify the following statement
- // accordingly...
- // I know, this clause is a pain in the ass: what it really says is this:
- // As long as we have a FORM Chunk and we don't have all of the {Formatversion, Common, SoundData} chunks
- // keep going... AND we haven't gotten an error from the file system.
- } while ( (chunkFlags & kFORM)
- && (!(chunkFlags & kCommon) || !(chunkFlags & kSoundData))
- && (err == noErr) );
-
- // If we completed normally, Position the mark at the beginning of the sound data
- if (chunkFlags && (err == noErr) ) {
- err = SetFPos (frefNum, fsFromStart, dataStart); // move mark to data start
- Assert ((err != noErr), "\pSetFPos failed");
- }
- if (err != noErr)
- dataSize = 0; // signal a failure
- return (dataSize);
- }
-
- //
- // Records from the currently selected sound input device into a file.
- // The error you get back can be from standardfile, file manager or the sound manager
- //
- OSErr RecordAIFFFile (OSType creator)
- {
- short fRefNum;
- OSErr err;
- Point here;
- StandardFileReply sReply;
-
- StandardPutFile ("\pRecord to:", "\pNew AIFF File", &sReply);
- Assert ((!sReply.sfGood), "\pFailed in standardfile");
-
- if (sReply.sfGood) { // Valid file selected?
-
- // Standard file survived the user!! Decide if the file exists or needs to be created
- if (sReply.sfReplacing) {
-
- err = FSpDelete (&sReply.sfFile);
- Assert (err, "\pCouldn't delete existing file");
- }
-
- // Now create a new file and then open it...
- err = FSpCreate (&sReply.sfFile, creator, 'AIFF',iuSystemScript);
- Assert (err, "\pCouldn't create file");
- if (err == noErr) {
- err = FSpOpenDF (&sReply.sfFile, fsRdWrPerm, &fRefNum);
- Assert (err, "\pCouldn't open file");
- }
- } else
- err = userCanceledErr; // No! return error...
-
- if (err == noErr) {
-
- // record to the file using the highest level routines available. This gives
- // us a ready made user interface....
- here.h = 30;
- here.v = 30;
- err = SndRecordToFile(nil, here, siBestQuality, fRefNum);
- Assert (err, "\pBombed out recording...");
-
- // Now that we're done (for better or for worse) close the file...
- FSClose (fRefNum);
- }
- return (err);
- }
-